//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Eas.WebCrawler.Interfaces;

namespace Eas.WebCrawler.PlugIns.TemporaryStorage {
    /// <summary>A <see cref="TemporaryStorage"/> that stores data in a memory stream.</summary>
    /// <remarks>Use this store when the results are small enough to store in memory.  <see cref="FileStorage"/> is slower but takes up much less memory.</remarks>
    public class MemoryStorage : Eas.WebCrawler.PlugIns.TemporaryStorage.StreamBasedStorage{

        internal class LengthTrackedStream : System.IO.MemoryStream {
            public const long LengthIfUnset = -3;
            public LengthTrackedStream() : base() {}
            public LengthTrackedStream(byte[] buffer, int index, int count, bool writable)
                : base(buffer, index, count, writable) {}

            /// <summary>The Length of the Stream before closing (in bytes).</summary>
            /// <remarks>Backing field for the TrackedLength property.</remarks>
            /// <seealso cref="TrackedLength"/>
            private long _TrackedLength = LengthIfUnset;

            /// <summary>The Length of the Stream before closing (in bytes).</summary>
            public long TrackedLength {
                get { return _TrackedLength; }
            }

            public override void Close() {
                if(_TrackedLength==LengthIfUnset)
                    _TrackedLength = this.Length;
                base.Close();
            }
        }

        LengthTrackedStream _Stream;
        byte[] _StreamBuffer;
        int _BufferLength;

        /// <summary>Disposes of this instance</summary>
        /// <param name="isDisposing">Whether we are disposing or finalizing.</param>
        protected override void Dispose(bool isDisposing) {
            try {
                if (isDisposing) {
                    KillStream();
                }
            }
            finally {
                base.Dispose(isDisposing);
            }
        }

        /// <summary>Gets the underlying memory stream for reading.</summary>
        /// <returns>The underlying file memory.</returns>
        protected override System.IO.Stream GetStreamForReading() {
            ResetStream();   
            return _Stream;
        }

        /// <summary>Gets the underlying memory stream for writting.</summary>
        /// <returns>The underlying memory stream.</returns>
        protected override System.IO.Stream GetStreamForWriting() {
            KillStream();
            KillBuffers();
            _Stream = new LengthTrackedStream();
            return _Stream;
        }

        private void ResetStream() {
            if (_StreamBuffer == null) {
                _StreamBuffer = _Stream.GetBuffer();
                _BufferLength = GetTrackedLengthOfStream();
            }
            KillStream();
            _Stream = new LengthTrackedStream(_StreamBuffer, 0, _BufferLength, false);
        }

        private int GetTrackedLengthOfStream() {
            System.Diagnostics.Debug.Assert(_Stream.TrackedLength > LengthTrackedStream.LengthIfUnset);
            return (int)_Stream.TrackedLength;
        }

        private void KillStream() {
            if (_Stream != null) {
                _Stream.Dispose();
                _Stream = null;
            }
        }

        private void KillBuffers() {
            _StreamBuffer = null;
            _BufferLength = 0;
        }
    }
    
    /// <summary>A <see cref="TemporaryStorageFactory"/> that creates a <see cref="MemoryStorage"/> to store data in.</summary>
    /// <remarks>Use this store factory when the results are small enough to store in memory.  <see cref="FileStorageFactory"/> is slower but takes up much less memory.</remarks>
    public class MemoryStorageFactory : Eas.WebCrawler.Interfaces.TemporaryStorageFactory {

        #region TemporaryStorageFactory Members
        /// <summary>Creates a new instance of a <see cref="MemoryStorage"/>.</summary>
        /// <param name="processingState">The <see cref="ProcessingState"/> for the current request.</param>
        /// <returns>A new instance of a <see cref="MemoryStorage"/>.</returns>
        public Eas.WebCrawler.Interfaces.TemporaryStorage Create(ProcessingState processingState) {
            return new Eas.WebCrawler.PlugIns.TemporaryStorage.MemoryStorage();
        }

        #endregion

        #region WebCrawlerComponent Members

        /// <summary>Initializes this class</summary>
        /// <param name="context">The <see cref="WebCrawlerContext"/> which will contain data about this instance of the web crawler.</param>
        public void Initialize(Eas.WebCrawler.Interfaces.WebCrawlerContext context) {
        }

        #endregion
    }
}
